/*! \file synchrony.h
    \brief Time-based indexing for a shared memory-based ring buffer.
	
	'Synapse' Data Sharing System | Flinders University | Martin Luerssen | 2009+
	--------- Last update: 1/12/10
*/

#ifndef _SYNCHRONY_H
#define _SYNCHRONY_H

#include "synapse.h"
#include <vector>
#include <limits>

#undef min
#undef max

using namespace std;

//-----------------------------------------------------------------------------
// CLASSES

//! Ring buffer parameters and state (shared between Synchrony objects).
struct SynchroInfo
{
	//! Default constructor.
	SynchroInfo() : bufferSize(0), interval(0), slots(0), gap(0), oldestSlot(0), oldestSlotTime(0), newestSlot(0), newestSlotTime(0), newestTime(0), newestIndex(0), samples(0), pushed(0) {}

	//! Size of buffer (in bytes).
	long bufferSize; 
	//! Interval between time slots.
	long long interval; 
	//! Total number of time slots that can be indexed.
	long slots; 
	//! Current number of free bytes between newest and oldest sample.
	long gap;
	//! Time slot that contains the oldest sample.
	long oldestSlot;
	//! Starting time of the oldest time slot.
	long long oldestSlotTime;
	//! Time slot that contains the newest sample.
	long newestSlot;
	//! Starting time of the newest time slot.
	long long newestSlotTime;
	//! Starting time of the newest sample.
	long long newestTime;
	//! Offset of newest sample.
	long newestIndex;
	//! Number of samples currently in the buffer.
	long samples;
	//! Total count of samples pushed onto this ring buffer (including now overwritten ones).
	long long pushed;
};

//! Time slot.
struct SynchroSlot
{
	//! Default constructor.
	SynchroSlot() : time(0), count(0), index(-1) {}

	//! Start time of the time slot.
	long long time;
	//! Number of samples starting within this time slot.
	long count; 
	//! Offset inside the ring buffer for where the first such sample is located.
	long index; 
};

//! Statistics for data sample inside the ring buffer.
struct SynchroSampleInfo
{
	//! Default constructor (all statistics are zero).
	SynchroSampleInfo() : startTime(0), size(0), id(-1) {}

	//! Start time of the sample.
	long long startTime;
	//! Size of the sample (as number of objects of template type).
	long size;
	//! Sample number (push order; unique identifier).
	long long id;

	void CopyToCharWrap(char* a_pFirst, char* a_pStart, char* a_pWrap) const;
	void ConvertFromCharWrap(char* a_pFirst, char* a_pStart, char* a_pWrap);
};

//! A data sample (including statistics) inside the ring buffer.
template <class T=unsigned char, class H=SynchroSampleInfo>
class SynchroSample
{
public:
	//! Default constructor (empty sample).
	SynchroSample() : m_pData(NULL), m_bExternalMemory(false) {}
	//! Destructor (deletes sample data).
	~SynchroSample() { if (!m_bExternalMemory) delete m_pData; }
	//! Get a reference to the sample statistics.
	H& Info() { return m_Info; }
	//! Get the start time of the sample.
	long long GetStartTime() const { return m_Info.startTime; }
	//! Get the unique id of the sample (which is allocated when the sample is pushed onto the ring buffer).
	long long GetId() const { return m_Info.id; }
	//! Get the size of the sample (as number of objects of template type).
	long GetSize() const { return m_Info.size; }
	//! Set the start time of the sample.
	void SetStartTime(long long a_StartTime = Synchrony<T>::Time()) { m_Info.startTime = a_StartTime; }
	//! Set a unique id for the sample.
	void SetId(long long a_Id) { m_Info.id = a_Id; }
	//! Indexing operator.
	T& operator[](size_t a_Index) { return m_pData[a_Index]; }
	//! Indexing operator (constant/read-only).
	const T& operator[](size_t a_Index) const { return m_pData[a_Index]; }
	//! Retrieve the specified element from the sample data.
	T GetData(size_t a_Index = 0) const { return m_pData[a_Index]; }
	//! Set the specified element of the sample data. No boundary checks and NO ALLOCATION!
	void ChangeData(const T& a_Value, size_t a_Index = 0) { m_pData[a_Index] = a_Value; }
	//! Returns pointer to sample data.
	T* GetDataPointer() { return m_pData; }
	
	SynchroSample(const T a_pArrayIn[], long a_Size = 1, long long a_StartTime = Synchrony<T>::Time());
	SynchroSample(bool a_bTakeOwnership, T* a_pData, long a_Size = 1, long long a_StartTime = Synchrony<T>::Time());
	SynchroSample(const T a_pArrayIn[], H a_Info);
	SynchroSample(bool a_bTakeOwnership, T* a_pData, H a_Info);
	SynchroSample(const SynchroSample<T, H>& a_rOther);
	SynchroSample<T, H>& operator=(const SynchroSample<T, H>& a_rOther);
	void CopyToCharWrap(char* a_pFirst, char* a_pStart, char* a_pWrap) const;
	void ConvertFromCharWrap(char* a_pFirst, char* a_pStart, char* a_pWrap);
	void GetAllData(T a_pArrayOut[]) const;
	bool GetDataRange(T a_pArrayOut[], long a_SampleIndex, long a_Size) const;
	void AllocateData(long a_Size);
	void SetAllData(const T a_pArrayIn[], long a_Size = 1);
	bool ChangeDataRange(const T a_pArrayIn[], long a_SampleIndex, long a_Size);
	void SetDataPointer(T* a_pData, long a_Size = 1, bool a_bTakeOwnership = false);

protected:
	//! Sample statistics.
	H m_Info;
	//! Pointer to sample data.
	T* m_pData;
	//! Indicates that the memory at the sample data pointer should not be managed by this class.
	bool m_bExternalMemory;
};

//! Shared memory ring buffer with time-based indexing.
template <class T=unsigned char, class H=SynchroSampleInfo>
class Synchrony
{
public:
	Synchrony();
	~Synchrony();

	static bool Exists(const char* a_pShareName);
	//! Short version of 4-argument Create(); replaces omitted arguments with user-set default parameters.
	bool Create(const char* a_pShareName) { return Create(a_pShareName, m_RawBufferSize, m_DesiredTimeSlots, m_DesiredInterval); }
	//! Short version of 4-argument Create(); replaces omitted arguments with user-set default parameters.
	bool Create(const char* a_pShareName, long a_BufferSize) { return Create(a_pShareName, a_BufferSize, m_DesiredTimeSlots, m_DesiredInterval); }
	//! Short version of 4-argument Create(); replaces omitted arguments with user-set default parameters.
	bool Create(const char* a_pShareName, long a_BufferSize, long a_TimeSlots) { return Create(a_pShareName, a_BufferSize, a_TimeSlots, m_DesiredInterval); }
	bool Create(const char* a_pShareName, long a_BufferSize, long a_TimeSlots, long long a_Interval);
	bool Open(const char* a_pShareName);
	bool WaitOpen(const char* a_pShareName, long a_PollWaitTime = 50);
	void Release();
	bool IsOpen() const;
	const char* GetName() const;
	static long long Time();

	void Reset();
	bool Push(SynchroSample<T, H>& a_rSample);
	bool PushTime(long long a_StartTime);
	bool ReadOldest(SynchroSample<T, H>& a_rSample);
	SynchroSample<T, H> ReadOldest();
	bool ReadNewest(SynchroSample<T, H>& a_rSample);
	bool WaitReadNewest(SynchroSample<T, H>& a_rSample);
	SynchroSample<T, H> ReadNewest();
	SynchroSample<T, H> WaitReadNewest();
	bool ReadNext(SynchroSample<T, H>& a_rSample);
	bool WaitReadNext(SynchroSample<T, H>& a_rSample);
	SynchroSample<T, H> ReadNext();
	SynchroSample<T, H> WaitReadNext();
	bool ReadAtTime(SynchroSample<T, H>& a_rSample, long a_StartTime, long a_Index = 0);
	bool WaitReadAtTime(SynchroSample<T, H>& a_rSample, long a_StartTime, long a_Index = 0);
	SynchroSample<T, H> ReadAtTime(long a_StartTime, long a_Index = 0);
	SynchroSample<T, H> WaitReadAtTime(long a_StartTime, long a_Index = 0);
	bool ReadFirstAtTimeOrPrevious(SynchroSample<T, H>& a_rSample, long a_StartTime);
	SynchroSample<T, H> ReadFirstAtTimeOrPrevious(long a_StartTime);
	bool ReadAllAtTime(vector<SynchroSample<T, H> >& a_rSampleArray, long a_StartTime);
	bool ReadAtSlot(SynchroSample<T, H>& a_rSample, long a_Slot, long a_Index = 0);
	SynchroSample<T, H> ReadAtSlot(long a_Slot, long a_Index = 0);
	bool ReadAllAtSlot(vector<SynchroSample<T, H> >& a_rSampleArray, long a_Slot);
	long CountAtTime(long a_StartTime);
	long CountAtSlot(long a_Slot);
	void ListSlotTimes(vector<long long>& a_rTimeArray);
	void ListSlotCounts(vector<long>& a_rCountArray);
	
	//! Size in bytes of the header preceding any sample (or sample array) pushed onto the ring buffer.
	static unsigned long GetSampleHeaderSize() { return sizeof(H); }
	//! Pointer to internal Synapse containing ring buffer.
	Synapse<char>& Buffer() { return m_Buffer; }
	SynchroInfo GetInfo();
	bool SetInterval(long long a_Interval = -1);
	bool SetTimeSlots(long a_TimeSlots = -1);
	bool SetBufferSizeInUnits(long a_Size);
	bool SetBufferSize(long a_Size);

protected:
	void _IncrementReaders();
	void _DecrementReaders(bool a_bSignalRead = true);
	bool _ReadNewest(bool a_bWait, SynchroSample<T, H>& a_rSample);
	bool _ReadNext(bool a_bWait, SynchroSample<T, H>& a_rSample);
	bool _ReadAtTime(bool a_bWait, SynchroSample<T, H>& a_rSample, long a_StartTime, long a_Index);
	bool _ReadFirstAtTimeOrPrevious(SynchroSample<T, H>& a_rSample, long a_StartTime);
	bool _ReadAllAtTime(bool a_bWait, vector<SynchroSample<T, H> >& a_rSampleArray, long a_StartTime);

	//! Synapse ring buffer.
	Synapse<char> m_Buffer;
	//! Desired interval between time slots (-actual- interval if Synchrony is created/opened).
	long long m_DesiredInterval;
	//! Desired number of time slots (-actual- number if Synchrony is created/opened).
	long m_DesiredTimeSlots;
	//! Desired buffer size in bytes (-actual- buffer size if Synchrony is created/opened).
	long m_RawBufferSize;
	//! Memory offset of next sample.
	long m_PoppedIndex;
	//! Id stamp of next sample.
	long long m_PoppedId;
};

//-----------------------------------------------------------------------------
// HELPER FUNCTIONS

//! Adds two values and 'wraps' if a maximum value is reached.
/*!
 * \param a_rIndex The variable that is to be modified.
 * \param a_Added A POSITIVE value to add to a_rIndex.
 * \param a_Minimum The value that a_rIndex is reset to if it reaches a_Maximum.
 * \param a_Maximum If a_rIndex reaches or exceeds this value, a_rIndex is reset to a_Minimum.
 */
template <class T>
void AddWrap(T& a_rIndex, long a_Added, T a_Minimum, T a_Maximum)
{
	a_rIndex += a_Added;
	if (a_rIndex >= a_Maximum)
	{
		a_rIndex += a_Minimum - a_Maximum;
	}
}

//-----------------------------------------------------------------------------
// SYNCHROSAMPLEINFO

//! Converts a SynchroSampleInfo into a character string.
/*!
 * \param a_pFirst Pointer to the first element in the character buffer.
 * \param a_pStart Pointer to where the SynchroSample should start inside the buffer.
 * \param a_pWrap Pointer to the end of the character buffer.
 */
inline
void SynchroSampleInfo::CopyToCharWrap(char* a_pFirst, char* a_pStart, char* a_pWrap) const
{
	const char* pSelf = reinterpret_cast<const char*>(this);
	for (size_t i = 0; i < sizeof(SynchroSampleInfo); i++)
	{
		*(a_pStart++) = *(pSelf++);
		if (a_pStart == a_pWrap)
		{
			a_pStart = a_pFirst;
		}
	}
}

//! Converts a character string to a SynchroSampleInfo.
/*!
 * \param a_pFirst Pointer to the first element in the entire buffer.
 * \param a_pStart Pointer to where the SynchroSampleInfo starts inside the buffer.
 * \param a_pWrap Pointer to the end of the character buffer.
 */
inline
void SynchroSampleInfo::ConvertFromCharWrap(char* a_pFirst, char* a_pStart, char* a_pWrap)
{
	char* pSelf = reinterpret_cast<char*>(this);
	for (size_t i = 0; i < sizeof(SynchroSampleInfo); i++)
	{
		*(pSelf++) = *(a_pStart++);
		if (a_pStart == a_pWrap)
		{
			a_pStart = a_pFirst;
		}
	}
}

//-----------------------------------------------------------------------------
// SYNCHROSAMPLE

//! Constructor with initial values.
/*!
* \param a_pArrayIn Array of data to store in this sample.
 * \param a_Size Size of the passed array (number of objects of template type).
 * \param a_StartTime Start time of sample.
 */
template <class T, class H>
SynchroSample<T, H>::SynchroSample(const T a_pArrayIn[], long a_Size, long long a_StartTime)
: m_pData(NULL),
  m_bExternalMemory(false)
{
	SetAllData(a_pArrayIn, a_Size);
	SetStartTime(a_StartTime);
}

//! Constructor for existing data.
/*!
 * \param a_bTakeOwnership If TRUE, passed memory object will automatically be deleted when not needed anymore.
 * \param a_pData Pointer to existing memory object.
 * \param a_Size Size of data (as number of objects of template type.
 * \param a_StartTime Start time of sample.
 */
template <class T, class H>
SynchroSample<T, H>::SynchroSample(bool a_bTakeOwnership, T* a_pData, long a_Size, long long a_StartTime)
{
	SetDataPointer(a_pData, a_Size, a_bTakeOwnership);
	SetStartTime(a_StartTime);
}

//! Constructor with initial custom sample information.
/*!
 * \param a_pArrayIn Array of data to store in this sample.
 * \param a_Size Size of the passed array (number of objects of template type).
 * \param a_Info Custom sample information (must include correct size of data).
 */
template <class T, class H>
SynchroSample<T, H>::SynchroSample(const T a_pArrayIn[], H a_Info)
: m_pData(NULL),
  m_bExternalMemory(false)
{
	SetAllData(a_pArrayIn, a_Info.size);
	Info() = a_Info;
}

//! Constructor for existing data (with custom sample information).
/*!
 * \param a_bTakeOwnership If TRUE, passed memory object will automatically be deleted when not needed anymore.
 * \param a_pData Pointer to existing memory object.
 * \param a_Info Custom sample information (must include correct size of data).
 */
template <class T, class H>
SynchroSample<T, H>::SynchroSample(bool a_bTakeOwnership, T* a_pData, H a_Info)
{
	SetDataPointer(a_pData, a_Info.size, a_bTakeOwnership);
	Info() = a_Info;
}

//! Copy constructor.
/*!
 * \param a_rOther Another Synchrony object from which to copy data. 
 */
template <class T, class H>
SynchroSample<T, H>::SynchroSample(const SynchroSample<T, H>& a_rOther)
: m_pData(NULL),
  m_bExternalMemory(false)
{
	m_Info = a_rOther.m_Info;
	if (m_Info.size)
	{
		m_pData = new T[m_Info.size];
		for (long i = 0; i < m_Info.size; i++)
		{
			m_pData[i] = a_rOther.m_pData[i];
		}
	}
}

//! Assignment operator.
/*!
 * \param a_rOther Another Synchrony object from which to assign data. 
 * \return Updated referene to self.
 */
template <class T, class H>
SynchroSample<T, H>& SynchroSample<T, H>::operator=(const SynchroSample<T, H>& a_rOther)
{
	if (m_pData != a_rOther.m_pData)
	{
		m_Info = a_rOther.m_Info;
		if (!m_bExternalMemory)
		{
			delete m_pData;
		}
		else
		{
			m_bExternalMemory = false;
		}
		if (m_Info.size > 0)
		{	
			m_pData = new T[m_Info.size];
			for (long i = 0; i < m_Info.size; i++)
			{
				m_pData[i] = a_rOther.m_pData[i];
			}
		}
		else
		{
			m_Info.size = 0;
			m_pData = NULL;
		}
	}
	return *this;
}

//! Converts a SynchroSample into a character string.
/*!
 * \param a_pFirst Pointer to the first element in the entire buffer.
 * \param a_pStart Pointer to where the SynchroSample should start inside the buffer.
 * \param a_pWrap Pointer to the end of the character buffer.
 */
template <class T, class H>
void SynchroSample<T, H>::CopyToCharWrap(char* a_pFirst, char* a_pStart, char* a_pWrap) const
{
	m_Info.CopyToCharWrap(a_pFirst, a_pStart, a_pWrap);
	char* pDataStart = a_pStart;
	AddWrap(pDataStart, sizeof(SynchroSampleInfo), a_pFirst, a_pWrap);
	char* pSampleData = reinterpret_cast<char*>(m_pData);
	long sizeOfNew = GetSize() * sizeof(T);
	while (sizeOfNew--)
	{
		*(pDataStart++) = *(pSampleData++);
		if (pDataStart == a_pWrap)
		{
			pDataStart = a_pFirst;
		}
	}
}

//! Converts a character string to a SynchroSample.
/*!
 * \param a_pFirst Pointer to the first element in the entire buffer.
 * \param a_pStart Pointer to where the SynchroSample starts inside the buffer.
 * \param a_pWrap Pointer to the end of the character buffer.
 */
template <class T, class H>
void SynchroSample<T, H>::ConvertFromCharWrap(char* a_pFirst, char* a_pStart, char* a_pWrap)
{
	m_Info.ConvertFromCharWrap(a_pFirst, a_pStart, a_pWrap);
	if (!m_bExternalMemory)
	{
		delete m_pData;
	}
	else
	{
		m_bExternalMemory = false;
	}
	if (m_Info.size <= 0)
	{
		m_Info.size = 0;
		m_pData = NULL;
		return;
	}
	m_pData = new T[m_Info.size];
	char* pSampleData = reinterpret_cast<char*>(m_pData);
	char* pDataStart = a_pStart;
	AddWrap(pDataStart, sizeof(SynchroSampleInfo), a_pFirst, a_pWrap);
	for (size_t i = 0; i < m_Info.size * sizeof(T); i++)
	{
		*(pSampleData++) = *(pDataStart++);
		if (pDataStart == a_pWrap)
		{
			pDataStart = a_pFirst;
		}
	}
}

//! Read the current sample data into a passed array.
/*!
 * \param a_pArrayOut Array (of sufficient size) to store the sample data.
 */
template <class T, class H>
inline
void SynchroSample<T, H>::GetAllData(T a_pArrayOut[]) const
{
	for (long i = 0; i < m_Info.size; i++)
	{
		a_pArrayOut[i] = m_pData[i];
	}
}

//! Read a specified subset of the current sample data into a passed array.
/*!
 * \param a_pArrayOut Array (of sufficient size) to store the sample data.
 * \param a_SampleIndex Index of the first data element (of the sample) to read into the array.
 * \param a_Size Number of data elements to read into array (array must have capacity for this).
 * \return TRUE if the specified data subset could be read (and did not exceed sample size); FALSE otherwise.
 */
template <class T, class H>
inline
bool SynchroSample<T, H>::GetDataRange(T a_pArrayOut[], long a_SampleIndex, long a_Size) const
{
	if (a_SampleIndex + a_Size <= m_Info.size)
	{
		for (long i = 0; i < a_Size; i++)
		{
			a_pArrayOut[i] = m_pData[a_SampleIndex+i];
		}
		return true;
	}
	return false;
}

//! Allocate memory in the sample to store data.
/*!
 * NB: Memory is not initialized and may have random or previous content.
 * \param a_Size Size of the memory to allocate (number of objects of template type).
 * \sa SetDataRange
 */
template <class T, class H>
inline
void SynchroSample<T, H>::AllocateData(long a_Size)
{
	if (!m_bExternalMemory)
	{
		delete m_pData;
	}
	if (a_Size > 0)
	{
		m_pData = new T[a_Size];
		m_Info.size = a_Size;
	}
	else
	{
		m_pData = NULL;
		m_Info.size = 0;
	}
	m_bExternalMemory = false;
}

//! Overwrite current sample data with the passed array.
/*!
 * Method allocates data, no explicit allocation needed.
 * \param a_pArrayIn Array of data to store in this sample.
 * \param a_Size Size of the passed array (number of objects of template type).
 */
template <class T, class H>
inline
void SynchroSample<T, H>::SetAllData(const T a_pArrayIn[], long a_Size)
{
	AllocateData(a_Size);
	for (long i = 0; i < a_Size; i++)
	{
		m_pData[i] = a_pArrayIn[i];
	}
}

//! Overwrite a specified subset of the current sample data with the passed array.
/*!
 * \param a_pArrayIn Array of data to store in this sample.
 * \param a_SampleIndex Index of the first data element (of the sample) to overwrite with content of the passed array.
 * \param a_Size Number of data elements to write into sample (passed array and allocated sample memory must have capacity for this).
 * \return TRUE if there was enough space in the sample to insert the passed data; FALSE otherwise.
 * \sa AllocateData
 */
template <class T, class H>
inline
bool SynchroSample<T, H>::ChangeDataRange(const T a_pArrayIn[], long a_SampleIndex, long a_Size)
{
	if (a_SampleIndex + a_Size <= m_Info.size)
	{
		for (long i = 0; i < a_Size; i++)
		{
			m_pData[a_SampleIndex+i] = a_pArrayIn[i];
		}
		return true;
	}
	return false;
}

//! Set the sample data to an existing memory location.
/*!
 * \param a_pData Pointer to existing memory object.
 * \param a_Size Size of data (as number of objects of template type.
 * \param a_bTakeOwnership If TRUE, passed memory object will automatically be deleted when not needed anymore.
 */
template <class T, class H>
inline
void SynchroSample<T, H>::SetDataPointer(T* a_pData, long a_Size, bool a_bTakeOwnership) 
{ 
	if (a_Size > 0)
	{
		if (!m_bExternalMemory)
		{
			delete m_pData;
		}
		m_pData = a_pData; 
		m_Info.size = a_Size; 
		m_bExternalMemory = !a_bTakeOwnership;
	}
}

//-----------------------------------------------------------------------------
// SYNCHRONY

//! Default constructor.
template <class T, class H>
Synchrony<T, H>::Synchrony()
: m_RawBufferSize(0),
  m_PoppedIndex(0),
  m_PoppedId(0)
{
	SetInterval();
	SetTimeSlots();
}

//! Destructor.
template <class T, class H>
Synchrony<T, H>::~Synchrony()
{
	Release();
}

//! Checks whether a Synchrony ring buffer is already being shared under the specified name.
/*!
 * \param a_pShareName Intended share name of Synchrony ring buffer.
 * \return TRUE if already sharing under this name; FALSE if not sharing or if
 * an existing Synchrony-incompatible Synapse with this name exists.
 */
template <class T, class H>
inline
bool Synchrony<T, H>::Exists(const char* a_pShareName)
{
	return Synapse<char>::Exists(a_pShareName);
}

//! Creates a new shared memory ring buffer.
/*!
 * \param a_pShareName Desired name of file and mutex.
 * \param a_BufferSize Size of buffer in bytes.
 * \param a_TimeSlots Maximum number of time slots (multiplied by a_Interval = total lenth of time line).
 * \param a_Interval Interval between each time slot.
 * \return TRUE if file and mutex were created successfully; FALSE if file and mutex already exist,
 * name is already used by the system, or memory allocation problems have occurred.
 */
template <class T, class H>
bool Synchrony<T, H>::Create(const char* a_pShareName, long a_BufferSize, long a_TimeSlots, long long a_Interval)
{
	m_DesiredTimeSlots = a_TimeSlots;
	m_DesiredInterval = a_Interval;
	m_RawBufferSize = a_BufferSize;

	if (m_DesiredInterval > 0 && m_DesiredTimeSlots > 0 && m_RawBufferSize > 0)
	{
		if (m_Buffer.Create(a_pShareName, sizeof(SynchroInfo) + a_TimeSlots * sizeof(SynchroSlot) + m_RawBufferSize, 0))
		{
			m_Buffer.Lock();
			// grab pointer to shared data
			char* pData = m_Buffer.GrabPointer();
			// global information about the Synchrony object is at the start of the data
			SynchroInfo* pInfo = reinterpret_cast<SynchroInfo*>(pData);
			// update shared variables to reflect shared Synchrony parameters
			pInfo->interval = m_DesiredInterval;
			pInfo->slots = m_DesiredTimeSlots;
			pInfo->bufferSize = m_RawBufferSize;
			m_Buffer.Unlock();
			return true;
		}
	}
	return false;
}


//! Opens an existing shared memory ring buffer.
/*!
 * \param a_pShareName Existing name of Synchrony ring buffer.
 * \return TRUE if ring buffer has been successfully opened; FALSE if the name is invalid, an
 * existing Synchrony-incompatible Synapse with this name exists, or the ring buffer is 
 * already open from a previous Open() call.
 */
template <class T, class H>
bool Synchrony<T, H>::Open(const char* a_pShareName)
{
	if (m_Buffer.OpenNow(a_pShareName))
	{
		_IncrementReaders();
		// grab pointer to shared data
		char* pData = m_Buffer.GrabPointer();
		// global information about the Synchrony object is at the start of the data
		SynchroInfo* pInfo = reinterpret_cast<SynchroInfo*>(pData);
		// update local variables to reflect shared Synchrony parameters
		m_DesiredInterval = pInfo->interval;
		m_DesiredTimeSlots = pInfo->slots;
		m_RawBufferSize = pInfo->bufferSize;
		_DecrementReaders();
		// theoretically possible to open Synapse before creation complete, so need to check...
		if (m_DesiredInterval > 0 && m_DesiredTimeSlots > 0 && m_RawBufferSize > 0)
		{
			return true;
		}
		Release();
	}
	return false;
}

//! Opens an existing shared memory ring buffer (waits if buffer not yet created/available).
/*!
 * \param a_pShareName Existing name of Synchrony ring buffer.
 * \param a_PollWaitTime Time to wait between checking for availability (in milliseconds).
 * \return TRUE if ring buffer has been successfully opened; FALSE if the name is invalid, an
 * existing Synchrony-incompatible Synapse with this name exists, or the ring buffer is 
 * already open from a previous Open() call.
 */
template <class T, class H>
bool Synchrony<T, H>::WaitOpen(const char* a_pShareName, long a_PollWaitTime)
{
	if (!a_pShareName) return false;
	while (!Open(a_pShareName))
	{
		Sleep(a_PollWaitTime);
		if (m_Buffer.LocalInfo().flags & SYNAPSE_STOPWAITING) return false;
	}
	return true;
}

//! Terminates access to the shared memory ring buffer.
template <class T, class H>
void Synchrony<T, H>::Release()
{
	m_Buffer.Release();
	m_RawBufferSize = 0;
}

//! Checks whether creation/opening of shared ring buffer succeeded.
/*! \return TRUE if successful, FALSE otherwise.
 */
template <class T, class H>
inline
bool Synchrony<T, H>::IsOpen() const
{
	return m_Buffer.IsOpen();
}

//! Retrieves the name of the shared ring buffer.
/*! \return Current name. Empty if ring buffer creation/opening failed.
 */
template <class T, class H>
inline
const char* Synchrony<T, H>::GetName() const
{
	return m_Buffer.GetName();
}

//! Retrieves current system time.
/*!
 * \return High-resolution time.
 */
template <class T, class H>
inline
long long Synchrony<T, H>::Time()
{
	return Synapse<T>::Time();
}

//----------------

//! Resets the time slots and ring buffer to their initial states.
template <class T, class H>
void Synchrony<T, H>::Reset()
{
	if (m_Buffer.IsOpen() // Synapse is shared
		&& m_Buffer.WriteLock()) // lock Synapse for writing action
	{
		// grab pointer to shared data
		char* pData = m_Buffer.GrabPointer();
		// reset all states
		for (size_t i = 0; i < sizeof(SynchroInfo) + m_DesiredTimeSlots * sizeof(SynchroSlot); i++)
		{
			pData[i] = 0;
		}
		// global information about the Synchrony object is at the start of the data
		SynchroInfo* pInfo = reinterpret_cast<SynchroInfo*>(pData);
		// update shared variables to reflect shared Synchrony parameters
		pInfo->interval = m_DesiredInterval;
		pInfo->slots = m_DesiredTimeSlots;
		pInfo->bufferSize = m_RawBufferSize;
		// notify other Synapses that the buffer has been updated (for Wait... methods)
		m_Buffer.SetAsWritten();
		// exit
		m_Buffer.Unlock();
	}
}

//! Pushes a new sample onto the ring buffer.
/*!
 * \param a_rSample New sample to store on the ring buffer.
 * \return TRUE if sample successfully recorded; FALSE if Synapse is closed or cancelled, sample has 
 * inappropriate size, or sample is older than the newest sample existing on the ring buffer.
 */
template <class T, class H>
bool Synchrony<T, H>::Push(SynchroSample<T, H>& a_rSample)
{
	if (m_Buffer.IsOpen() // Synapse is shared
		&& (a_rSample.GetSize() * sizeof(T) <= m_RawBufferSize - GetSampleHeaderSize())  // data is not more than can be stored in the Synapse
		&& m_Buffer.WriteLock()) // lock Synapse for writing action
	{
		// grab pointer to shared data
		char* pData = m_Buffer.GrabPointer();
		// global information about the Synchrony object is at the start of the data
		SynchroInfo* pInfo = reinterpret_cast<SynchroInfo*>(pData);
		pData += sizeof(SynchroInfo);
		// the timeline (array of SynchroSlots) follows the global information
		SynchroSlot* pSlots = reinterpret_cast<SynchroSlot*>(pData);
		// this is followed by the actual samples
		pData += pInfo->slots * sizeof(SynchroSlot);
		// buffer contains samples?
		if (pInfo->samples)
		{
			// newest existing sample in buffer
			char* pNewestStart = pData + pInfo->newestIndex;
			// convert from shared character stream to info object
			H newestSampleInfo;
			newestSampleInfo.ConvertFromCharWrap(pData, pNewestStart, pData + m_RawBufferSize);
			H oldestSampleInfo2;
			oldestSampleInfo2.ConvertFromCharWrap(pData, pData + (pSlots+pInfo->oldestSlot)->index, pData + m_RawBufferSize);
			// new samples cannot be older than existing samples
			if (newestSampleInfo.startTime <= a_rSample.GetStartTime())
			{
				// size of passed sample in bytes
				long sizeOfArg = GetSampleHeaderSize() + a_rSample.GetSize() * sizeof(T);
				// include gap between oldest and newest sample as part of free memory
				long unallocated = pInfo->gap;
				// how many time slots will be skipped because the sample is newer?
				long slotsToJump = static_cast<long>((a_rSample.GetStartTime() - pInfo->newestSlotTime) / pInfo->interval);
				// remove existing samples in slots that need to be skipped
				long jumped = 0;
				while (jumped < slotsToJump)
				{
					AddWrap(pInfo->newestSlot, 1, 0L, pInfo->slots);
					// slot is cleared
					jumped++;
					if (pInfo->newestSlot == pInfo->oldestSlot)
					{
						// must clear oldest slot
						SynchroSlot* pOldestSlot = pSlots + pInfo->oldestSlot;
						// new time
						pOldestSlot->time = pInfo->newestSlotTime + jumped * pInfo->interval;
						// for every sample in this slot...
						while (pOldestSlot->count)
						{				
							H oldestSampleInfo;
							oldestSampleInfo.ConvertFromCharWrap(pData, pData + pOldestSlot->index, pData + m_RawBufferSize);
							// determine the length of that whole sample
							long length = GetSampleHeaderSize() + oldestSampleInfo.size * sizeof(T);
							// record this as being free space
							unallocated += length;
							// reduce the sample count of the buffer
							pInfo->samples--;
							// move the pointer to the oldest sample to the next sample in the buffer
							AddWrap(pOldestSlot->index, length, 0L, m_RawBufferSize);
							// clear next sample
							pOldestSlot->count--;
						}
						AddWrap(pInfo->oldestSlot, 1, 0L, pInfo->slots);
					}
				}
				if (a_rSample.GetSize() > 0)
				{
					// oldest time slot now
					SynchroSlot* pOldestSlot = pSlots + pInfo->oldestSlot;
					// remove further samples until you can fit the new one
					while (unallocated < sizeOfArg)
					{
						if (pOldestSlot->count > 0)
						{
							// locate statistics for the oldest sample in the buffer
							H oldestSampleInfo;
							oldestSampleInfo.ConvertFromCharWrap(pData, pData + pOldestSlot->index, pData + m_RawBufferSize);
							// determine the length of that whole sample
							long length = GetSampleHeaderSize() + oldestSampleInfo.size * sizeof(T);
							// record this as being free space
							unallocated += length;
							// reduce the sample count of the buffer and the slot
							pInfo->samples--;
							pOldestSlot->count--;
							// move the pointer to the oldest sample to the next sample in the buffer
							AddWrap(pOldestSlot->index, length, 0L, m_RawBufferSize);
						}
						if (pOldestSlot->count == 0)
						{
							// slot is cleared
							jumped++;
							// new time
							pOldestSlot->time = pInfo->newestSlotTime + jumped * pInfo->interval;
							// next slot (oldest)
							AddWrap(pInfo->oldestSlot, 1, 0L, pInfo->slots);
							pOldestSlot = pSlots + pInfo->oldestSlot;
						}
					}
					if (pInfo->samples)
					{
						// record new gap between oldest and newest sample
						pInfo->gap = unallocated - sizeOfArg;
						// this is where the new sample starts
						AddWrap(pInfo->newestIndex, GetSampleHeaderSize() + newestSampleInfo.size * sizeof(T), 0L, m_RawBufferSize);
						// mark sample with current push order (unique identifier)
						a_rSample.SetId(pInfo->pushed++);
						// increase the sample count of the buffer
						pInfo->samples++;
						// copy sample
						a_rSample.CopyToCharWrap(pData, pData + pInfo->newestIndex, pData + m_RawBufferSize);
						// the time slot where the new sample will be located
						//AddWrap(pInfo->newestSlot, slotsToJump, 0L, pInfo->slots);
						// update time caches
						pInfo->oldestSlotTime = pOldestSlot->time;
						pInfo->newestSlotTime += slotsToJump * pInfo->interval;
						pInfo->newestTime = a_rSample.GetStartTime();
						// record index of the newest sample
						SynchroSlot* pCurrentSlot = pSlots + pInfo->newestSlot;
						if (pCurrentSlot->count++ == 0)
						{
							pCurrentSlot->index = pInfo->newestIndex;
							pCurrentSlot->time = pInfo->newestSlotTime;
						}
						// notify other Synapses that the buffer has been updated (for Wait... methods)
						m_Buffer.SetAsWritten();
						// exit
						m_Buffer.Unlock();
						return true;
					}
				}
				else
				{
					pInfo->pushed++; // empty samples are also counted (but not added to ring buffer)
				}
			}
		}
		// no samples in buffer
		if (!pInfo->samples)
		{
			// update slot
			pSlots->count = 1;
			pSlots->index = 0;
			if ((!pInfo->pushed) // a "virgin" ring buffer
				|| (a_rSample.GetStartTime() < pSlots->time)) // request a reset to an earlier time
			{
				// first-sample-ever determines initial time slot
				pSlots->time = a_rSample.GetStartTime();
			}
			else
			{
				// otherwise relative to existing interval blocks
				pSlots->time += ((a_rSample.GetStartTime() - pSlots->time) / pInfo->interval ) * pInfo->interval;
			}
			// update other info
			pInfo->oldestSlot = pInfo->newestSlot = 0;
			pInfo->oldestSlotTime = pInfo->newestSlotTime = pSlots->time;
			pInfo->newestIndex = 0;
			pInfo->newestTime = a_rSample.GetStartTime();
			if (a_rSample.GetSize() > 0)
			{
				pInfo->gap = m_RawBufferSize - (GetSampleHeaderSize() + a_rSample.GetSize() * sizeof(T));
				// mark sample with current push order (unique identifier)
				a_rSample.SetId(pInfo->pushed++);
				// increase the sample count of the buffer
				pInfo->samples = 1;
				// copy sample
				a_rSample.CopyToCharWrap(pData, pData, pData + m_RawBufferSize);
			}
			else
			{
				pInfo->pushed++; // empty samples are also counted (but not added to ring buffer)
			}
			// notify other Synapses that the buffer has been updated (for Wait... methods)
			m_Buffer.SetAsWritten();
			// exit
			m_Buffer.Unlock();
			return true;
		}
		m_Buffer.Unlock();
	}
	return false;
}

//! Update time slots as if a sample with the specified time was pushed onto the buffer.
/*!
 * Equivalent to pushing an empty sample onto the ring buffer (it won't be stored).
 * \param a_StartTime Starting time stamp of the hypothetical sample.
 * \return TRUE if time slots successfully updated; FALSE otherwise.
 */
template <class T, class H>
bool Synchrony<T, H>::PushTime(long long a_StartTime)
{
	SynchroSample<T, H> sample;
	sample.SetStartTime(a_StartTime);
	return Push(sample);
}

//! Reads the oldest sample from the ring buffer, and copies it into the passed sample.
/*!
 * \param a_rSample Passed sample object that will store the retrieved sample.
 * \return TRUE if sample successfully retrieved; FALSE if Synapse is closed or cancelled,
 * or there is no sample in the buffer.
 */
template <class T, class H>
bool Synchrony<T, H>::ReadOldest(SynchroSample<T, H>& a_rSample)
{
	if (m_Buffer.IsOpen()) // Synapse is shared
	{
		// signal read access to buffer
		_IncrementReaders();
		// grab pointer to shared data
		char* pData = m_Buffer.GrabPointer();
		// global information about the Synchrony object is at the start of the data
		SynchroInfo* pInfo = reinterpret_cast<SynchroInfo*>(pData);
		// buffer must contain samples
		if (pInfo->samples)
		{
			pData += sizeof(SynchroInfo);
			// the timeline (array of SynchroSlots) follows the global information
			SynchroSlot* pSlots = reinterpret_cast<SynchroSlot*>(pData);
			// this is followed by the actual samples...
			pData += pInfo->slots * sizeof(SynchroSlot);
			// get oldest sample offset
			char* pStart = pData + (pSlots + pInfo->oldestSlot)->index;
			// copy sample
			a_rSample.ConvertFromCharWrap(pData, pStart, pData + m_RawBufferSize);
			// update popped state
			AddWrap(pStart, GetSampleHeaderSize() + a_rSample.GetSize() * sizeof(T), pData, pData + m_RawBufferSize);
			m_PoppedIndex = static_cast<long>(pStart - pData);
			m_PoppedId = a_rSample.GetId() + 1;
			_DecrementReaders();
			return true;
		}
		_DecrementReaders();
	}
	return false;
}

//! Reads the oldest sample from the ring buffer, and returns it.
/*!
 * \return The newest sample, or an empty sample if Synapse is closed or cancelled,
 * or there is no sample in the buffer.
 */
template <class T, class H>
inline
SynchroSample<T, H> Synchrony<T, H>::ReadOldest()
{
	SynchroSample<T, H> sample;
	ReadOldest(sample);
	return sample;
}


//! Reads the newest sample from the ring buffer, and copies it into the passed sample.
/*!
 * \param a_rSample Passed sample object that will store the retrieved sample.
 * \return TRUE if sample successfully retrieved; FALSE if Synapse is closed or cancelled,
 * or there is no sample in the buffer.
 */
template <class T, class H>
inline
bool Synchrony<T, H>::ReadNewest(SynchroSample<T, H>& a_rSample)
{
	return _ReadNewest(false, a_rSample);
}

//! Reads the newest sample from the ring buffer, and copies it into the passed sample. 
//! Will wait for the next sample if the last read sample is the newest sample.
/*!
 * \param a_rSample Passed sample object that will store the retrieved sample.
 * \return TRUE if sample successfully retrieved; FALSE if Synapse is closed or cancelled.
 */
template <class T, class H>
inline
bool Synchrony<T, H>::WaitReadNewest(SynchroSample<T, H>& a_rSample)
{
	return _ReadNewest(true, a_rSample);
}

//! Reads the newest sample from the ring buffer, and returns it.
/*!
 * \return The newest sample, or an empty sample if Synapse is closed or cancelled,
 * or there is no sample in the buffer.
 */
template <class T, class H>
inline
SynchroSample<T, H> Synchrony<T, H>::ReadNewest()
{
	SynchroSample<T, H> sample;
	ReadNewest(sample);
	return sample;
}

//! Reads the newest sample from the ring buffer, and returns it.
//! Will wait for the next sample if the last read sample is the newest sample.
/*!
 * \return The newest sample, or an empty sample if Synapse is closed or cancelled.
 */
template <class T, class H>
inline
SynchroSample<T, H> Synchrony<T, H>::WaitReadNewest()
{
	SynchroSample<T, H> sample;
	WaitReadNewest(sample);
	return sample;
}

//! Reads the sample that follows the previously read sample, and copies it into the passed sample.
/*!
 * \param a_rSample Passed sample object that will store the retrieved sample.
 * \return TRUE if sample successfully retrieved; FALSE if Synapse is closed or cancelled,
 * or there is no next sample in the buffer.
 */
template <class T, class H>
inline
bool Synchrony<T, H>::ReadNext(SynchroSample<T, H>& a_rSample)
{
	return _ReadNext(false, a_rSample);
}

//! Reads the sample that follows the previously read sample, and copies it into the passed sample.
/*!
 * If there is no next sample, this method waits for it.
 * \param a_rSample Passed sample object that will store the retrieved sample.
 * \return TRUE if sample successfully retrieved; FALSE if Synapse is closed or cancelled.
 */
template <class T, class H>
inline
bool Synchrony<T, H>::WaitReadNext(SynchroSample<T, H>& a_rSample)
{
	return _ReadNext(true, a_rSample);
}

//! Reads the oldest sample from the ring buffer that was not read previously, and returns it.
/*!
 * \return The next sample, or an empty sample if Synapse is closed or cancelled,
 * or there is no next sample in the buffer.
 */
template <class T, class H>
inline
SynchroSample<T, H> Synchrony<T, H>::ReadNext()
{
	SynchroSample<T, H> sample;
	ReadNext(sample);
	return sample;
}


//! Reads the oldest sample from the ring buffer that was not read previously, and returns it.
/*!
 * If there is no next sample, this method waits for it.
 * \return The next sample, or an empty sample if Synapse is closed or cancelled.
 */
template <class T, class H>
inline
SynchroSample<T, H> Synchrony<T, H>::WaitReadNext()
{
	SynchroSample<T, H> sample;
	WaitReadNext(sample);
	return sample;
}

//! Retrieves a sample in the same time slot as the specified time, and copies it into the passed sample.
/*!
 * \param a_rSample Passed sample object that will store the retrieved sample.
 * \param a_StartTime The sample will be retrieved from the time slot that incorporates this time.
 * \param a_Index If there are multiple samples in the time slot, specifiy which sample you want (default is 0, i.e. first instance).
 * \return TRUE if the requested sample exists; FALSE otherwise.
 */
template <class T, class H>
inline
bool Synchrony<T, H>::ReadAtTime(SynchroSample<T, H>& a_rSample, long a_StartTime, long a_Index)
{
	return _ReadAtTime(false, a_rSample, a_StartTime, a_Index);
}

//! Retrieves a sample in the same time slot as the specified time, and copies it into the passed sample.
/*!
 * If the requested sample time is newer than that of the newest sample in the buffer, this method will wait for it.
 * \param a_rSample Passed sample object that will store the retrieved sample.
 * \param a_StartTime The sample will be retrieved from the time slot that incorporates this time.
 * \param a_Index If there are multiple samples in the time slot, specifiy which sample you want (default is 0, i.e. first instance).
 * \return TRUE if the requested sample exists; FALSE otherwise.
 */
template <class T, class H>
inline
bool Synchrony<T, H>::WaitReadAtTime(SynchroSample<T, H>& a_rSample, long a_StartTime, long a_Index)
{
	return _ReadAtTime(true, a_rSample, a_StartTime, a_Index);
}

//! Retrieves a sample in the same time slot as the specified time, and returns it.
/*!
 * \param a_StartTime The sample will be retrieved from the time slot that incorporates this time.
 * \param a_Index If there are multiple samples in the time slot, specifiy which sample you want (default is 0, i.e. first instance).
 * \return The retrieved sample, or an empty sample if none could be found.
 */
template <class T, class H>
inline
SynchroSample<T, H> Synchrony<T, H>::ReadAtTime(long a_StartTime, long a_Index)
{
	SynchroSample<T, H> sample;
	ReadAtTime(sample, a_StartTime, a_Index);
	return sample;
}

//! Retrieves a sample in the same time slot as the specified time, and returns it.
/*!
 * If the requested sample time is newer than that of the newest sample in the buffer, this method will wait for it.
 * \param a_StartTime The sample will be retrieved from the time slot that incorporates this time.
 * \param a_Index If there are multiple samples in the time slot, specifiy which sample you want (default is 0, i.e. first instance).
 * \return The retrieved sample, or an empty sample if none could be found.
 */
template <class T, class H>
inline
SynchroSample<T, H> Synchrony<T, H>::WaitReadAtTime(long a_StartTime, long a_Index)
{
	SynchroSample<T, H> sample;
	WaitReadAtTime(sample, a_StartTime, a_Index);
	return sample;
}

//! Retrieves the first sample in the same time slot as the specified time or the last sample existing before this time slot, and copies it into the passed sample.
/*!
 * \param a_rSample Passed sample object that will store the retrieved sample.
 * \param a_StartTime The sample will be retrieved from the time slot that incorporates this time (or the last time slot that contains a sample).
 * \return TRUE if a suitable sample was found; FALSE otherwise.
 */
template <class T, class H>
inline
bool Synchrony<T, H>::ReadFirstAtTimeOrPrevious(SynchroSample<T, H>& a_rSample, long a_StartTime)
{
	return _ReadFirstAtTimeOrPrevious(a_rSample, a_StartTime);
}

//! Retreives the first sample in the same time slot as the specified time or the last sample existing before this time slot, and returns it.
/*!
 * \param a_rSample Passed sample object that will store the retrieved sample.
 * \param a_StartTime The sample will be retrieved from the time slot that incorporates this time (or the last time slot that contains a sample).
 * \return The retrieved sample, or an empty sample if none could be found.
 */
template <class T, class H>
inline
SynchroSample<T, H> Synchrony<T, H>::ReadFirstAtTimeOrPrevious(long a_StartTime)
{
	SynchroSample<T, H> sample;
	ReadFirstAtTimeOrPrevious(sample, a_StartTime);
	return sample;
}

//! Retrieves all samples in the same time slot as the specified time.
/*!
 * \param a_rArrayOut An array to store all the retrieved samples.
 * \param a_StartTime The samples will be retrieved from the time slot that incorporates this time.
 * \return TRUE if the requested samples exist; FALSE otherwise.
 */
template <class T, class H>
inline
bool Synchrony<T, H>::ReadAllAtTime(vector<SynchroSample<T, H> >& a_rArrayOut, long a_StartTime)
{
	return _ReadAllAtTime(false, a_rArrayOut, a_StartTime);
}

//! Retrieves a sample in the specified time slot, and copies it into the passed sample.
/*!
 * \param a_rSample Passed sample object that will store the retrieved sample.
 * \param a_Slot The sample will be retrieved from this time slot.
 * \param a_Index If there are multiple samples in the time slot, specifiy which sample you want (default is 0, i.e. first instance).
 * \return TRUE if the requested sample exists; FALSE otherwise.
 */
template <class T, class H>
bool Synchrony<T, H>::ReadAtSlot(SynchroSample<T, H>& a_rSample, long a_Slot, long a_Index = 0)
{
	if (a_Slot < m_DesiredTimeSlots // time slot exists
		&& m_Buffer.IsOpen()) // Synapse is shared
	{
		// signal read access to buffer
		_IncrementReaders();
		// grab pointer to shared data
		char* pData = m_Buffer.GrabPointer();
		// global information about the Synchrony object is at the start of the data
		SynchroInfo* pInfo = reinterpret_cast<SynchroInfo*>(pData);
		// buffer must contain samples
		if (pInfo->samples)
		{
			pData += sizeof(SynchroInfo);
			// the timeline (array of SynchroSlots) follows the global information
			SynchroSlot* pSlots = reinterpret_cast<SynchroSlot*>(pData);
			// this is followed by the actual samples...
			pData += pInfo->slots * sizeof(SynchroSlot);
			// is there anything in the time slot?
			if (a_Index < pSlots[a_Slot].count)
			{
				char* pStart = pData + pSlots[a_Slot].index;
				for (long i = 0; i < a_Index; i++)
				{
					H sampleInfo;
					sampleInfo.ConvertFromCharWrap(pData, pStart, pData + m_RawBufferSize);
					AddWrap(pStart, GetSampleHeaderSize() + sampleInfo.size * sizeof(T), pData, pData + m_RawBufferSize);
				}
				// copy it into the sample object
				a_rSample.ConvertFromCharWrap(pData, pStart, pData + m_RawBufferSize);
				// updated popped state
				AddWrap(pStart, GetSampleHeaderSize() + a_rSample.GetSize() * sizeof(T), pData, pData + m_RawBufferSize);	
				m_PoppedIndex = static_cast<long>(pStart - pData);
				m_PoppedId = a_rSample.GetId() + 1;
				// exit successfully
				_DecrementReaders();
				return true;
			}
		}
		_DecrementReaders();
	}
	return false;
}

//! Retrieves a sample in the specified time slot, and returns it.
/*!
 * \param a_Slot The sample will be retrieved from this time slot.
 * \param a_Index If there are multiple samples in the time slot, specifiy which sample you want (default is 0, i.e. first instance).
 * \return The retrieved sample, or an empty sample if the slot is empty.
 */
template <class T, class H>
inline
SynchroSample<T, H> Synchrony<T, H>::ReadAtSlot(long a_Slot, long a_Index = 0)
{
	SynchroSample<T, H> sample;
	ReadAtSlot(sample, a_Slot, a_Index);
	return sample;
}


//! Retrieves all samples in the specified time slot.
/*!
 * \param a_rSampleArray An array to store all the retrieved samples.
 * \param a_Slot The samples will be retrieved from this time slot.
 * \return TRUE if the requested samples exist; FALSE otherwise.
 */
template <class T, class H>
bool Synchrony<T, H>::ReadAllAtSlot(vector<SynchroSample<T, H> >& a_rSampleArray, long a_Slot)
{
	if (a_Slot < m_DesiredTimeSlots // time slot exists
		&& m_Buffer.IsOpen()) // Synapse is shared
	{
		// signal read access to buffer
		_IncrementReaders();
		// grab pointer to shared data
		char* pData = m_Buffer.GrabPointer();
		// global information about the Synchrony object is at the start of the data
		SynchroInfo* pInfo = reinterpret_cast<SynchroInfo*>(pData);
		// buffer must contain samples
		if (pInfo->samples)
		{
			pData += sizeof(SynchroInfo);
			// the timeline (array of SynchroSlots) follows the global information
			SynchroSlot* pSlots = reinterpret_cast<SynchroSlot*>(pData);
			// this is followed by the actual samples...
			pData += pInfo->slots * sizeof(SynchroSlot);
			// is there anything in the time slot?
			if (pSlots[a_Slot].count)
			{
				char* pStart = pData + pSlots[a_Slot].index;
				for (long i = 0; i < pSlots[a_Slot].count; i++)
				{
					SynchroSample<T, H> sample;
					sample.ConvertFromCharWrap(pData, pStart, pData + m_RawBufferSize);
					a_rSampleArray.push_back(sample);
					AddWrap(pStart, GetSampleHeaderSize() + sample.GetSize() * sizeof(T), pData, pData + m_RawBufferSize);
				}
				// updated popped state
				m_PoppedIndex = static_cast<long>(pStart - pData);
				m_PoppedId = a_rSampleArray.back().GetId() + 1;
				// exit successfully
				_DecrementReaders();
				return true;
			}
		}
		_DecrementReaders();
	}
	return false;
}

//! Count the number of samples in the same time slot as the specified time.
/*!
 * \param a_StartTime Samples will be counted for the time slot that incorporates this time.
 * \return The number of samples in the same time slot as the specified time.
 */
template <class T, class H>
long Synchrony<T, H>::CountAtTime(long a_StartTime)
{
	long count = 0;
	if (m_Buffer.IsOpen()) // Synapse is shared
	{
		// signal read access to buffer
		_IncrementReaders();
		// grab pointer to shared data
		char* pData = m_Buffer.GrabPointer();
		// global information about the Synchrony object is at the start of the data
		SynchroInfo* pInfo = reinterpret_cast<SynchroInfo*>(pData);
		// buffer must contain samples
		if (pInfo->samples)
		{
			pData += sizeof(SynchroInfo);
			// the timeline (array of SynchroSlots) follows the global information
			SynchroSlot* pSlots = reinterpret_cast<SynchroSlot*>(pData);
			// temporal offset from earliest time in buffer
			long long diffTime = a_StartTime - pInfo->oldestSlotTime;
			// time has to be within range of what is currently indexed
			if ((diffTime >= 0) && (a_StartTime < pInfo->newestSlotTime+pInfo->interval))
			{
				// determine the offset in terms of slots
				long n = static_cast<long>(diffTime / pInfo->interval) % pInfo->slots;
				// find the slot that matches the specified time
				long actual_n = pInfo->oldestSlot;
				AddWrap(actual_n, n, 0L, pInfo->slots);
				// return sample count
				count = pSlots[actual_n].count;
			}
		}
		_DecrementReaders();
	}
	return count;
}

//! Count the number of samples in the specified time slot.
/*!
 * \param a_Slot The time slot for which to count samples.
 * \return The number of samples in the time slot.
 */
template <class T, class H>
long Synchrony<T, H>::CountAtSlot(long a_Slot)
{
	long count = 0;
	if (a_Slot < m_DesiredTimeSlots // time slot exists
		&& m_Buffer.IsOpen()) // Synapse is shared
	{
		// signal read access to buffer
		_IncrementReaders();
		// grab pointer to shared data
		char* pData = m_Buffer.GrabPointer();
		// global information about the Synchrony object is at the start of the data
		SynchroInfo* pInfo = reinterpret_cast<SynchroInfo*>(pData);
		// buffer must contain samples
		if (pInfo->samples)
		{
			pData += sizeof(SynchroInfo);
			// the timeline (array of SynchroSlots) follows the global information
			SynchroSlot* pSlots = reinterpret_cast<SynchroSlot*>(pData);
			// return sample count
			count = pSlots[a_Slot].count;
		}
		_DecrementReaders();
	}
	return count;
}

//! Retrieves all the starting times of the time slots.
/*!
 * \param a_rTimeArray The starting time of each time slot is stored in this array.
 */
template <class T, class H>
void Synchrony<T, H>::ListSlotTimes(vector<long long>& a_rTimeArray)
{
	if (m_Buffer.IsOpen()) // Synapse is shared
	{
		// signal read access to buffer
		_IncrementReaders();
		// grab pointer to shared data
		char* pData = m_Buffer.GrabPointer();
		// global information about the Synchrony object is at the start of the data
		SynchroInfo* pInfo = reinterpret_cast<SynchroInfo*>(pData);
		pData += sizeof(SynchroInfo);
		// the timeline (array of SynchroSlots) follows the global information
		SynchroSlot* pSlots = reinterpret_cast<SynchroSlot*>(pData);
		// init the passed array
		a_rTimeArray.clear();
		a_rTimeArray.reserve(m_DesiredTimeSlots);
		// copy slot times into passed array
		for (long i = 0; i < m_DesiredTimeSlots; i++)
		{
			a_rTimeArray.push_back(pSlots[i].time);
		}
		_DecrementReaders();
	}
}

//! Counts the number of samples in each time slot.
/*!
 * \param a_rCountArray The sample count for each time slot is stored in this array.
 */
template <class T, class H>
void Synchrony<T, H>::ListSlotCounts(vector<long>& a_rCountArray)
{
	if (m_Buffer.IsOpen()) // Synapse is shared
	{
		// signal read access to buffer
		_IncrementReaders();
		// grab pointer to shared data
		char* pData = m_Buffer.GrabPointer();
		// global information about the Synchrony object is at the start of the data
		SynchroInfo* pInfo = reinterpret_cast<SynchroInfo*>(pData);
		pData += sizeof(SynchroInfo);
		// the timeline (array of SynchroSlots) follows the global information
		SynchroSlot* pSlots = reinterpret_cast<SynchroSlot*>(pData);
		// init the passed array
		a_rCountArray.clear();
		a_rCountArray.reserve(m_DesiredTimeSlots);
		// copy slot times into passed array
		for (long i = 0; i < m_DesiredTimeSlots; i++)
		{
			a_rCountArray.push_back(pSlots[i].count);
		}
		_DecrementReaders();
	}
}

//! Collect the current statistics for the Synchrony ring buffer.
/*!
 * \return An object containing the ringbuffer statistics.
 */
template <class T, class H>
inline
SynchroInfo Synchrony<T, H>::GetInfo()
{
	SynchroInfo info;
	if (m_Buffer.IsOpen())
	{
		_IncrementReaders();
		info = *(reinterpret_cast<SynchroInfo*>(m_Buffer.GrabPointer()));
		_DecrementReaders();
	}
	return info;
}

//! Set the desired time interval between time slots.
/*!
 * This will only affect actual buffer if you create a new buffer after calling this method.
 * \param a_Interval Desired time interval (if argument omitted or <0, default is maximum interval).
 * \return FALSE if you have already created a buffer (no change possible).
 */
template <class T, class H>
bool Synchrony<T, H>::SetInterval(long long a_Interval)
{
	if (!m_Buffer.IsOpen() && a_Interval)
	{
		if (a_Interval < 0)
		{
			m_DesiredInterval = numeric_limits<long long>::max();
		}
		else
		{
			m_DesiredInterval = a_Interval;
		}
		return true;
	}
	return false;
}

//! Set the desired number of time slots on the time line used for indexing.
/*!
 * This will only affect actual buffer if you create a new buffer after calling this method.
 * \param a_TimeSlots Desired number of time slots (if argument omitted or <0, default is 1).
 * \return FALSE if you have already created a buffer (no change possible).
 */
template <class T, class H>
bool Synchrony<T, H>::SetTimeSlots(long a_TimeSlots)
{
	if (!m_Buffer.IsOpen() && a_TimeSlots)
	{
		if (a_TimeSlots < 0)
		{
			m_DesiredTimeSlots = 1;
		}
		else
		{
			m_DesiredTimeSlots = a_TimeSlots;
		}
		return true;
	}
	return false;
}

//! Set the desired size of the ring buffer (as the minimum number of template type objects to store).
/*!
 * This will only affect actual buffer if you create a new buffer after calling this method.
 * NB: If arrays of the template type objects are stored in the buffer (rather than individual objects), 
 * actual buffer size may greatly exceed the desired size.
 * \param a_Size Desired size of buffer (as number of objects of template type).
 * \return FALSE if you have already created a buffer (no change possible).
 */
template <class T, class H>
bool Synchrony<T, H>::SetBufferSizeInUnits(long a_Size)
{
	return SetBufferSize(a_Size * (sizeof(T) + GetSampleHeaderSize()));
}

//! Set the desired size of the ring buffer (in actual bytes).
/*!
 * This will only affect actual buffer if you create a new buffer after calling this method.
 * NB: Additional memory is allocated beyond the buffer to store the timeline and Synchrony state parameters.
 * \param a_Size Desired size of buffer (in bytes).
 * \return FALSE if you have already created a buffer (no change possible).
 */
template <class T, class H>
bool Synchrony<T, H>::SetBufferSize(long a_Size)
{
	if (!m_Buffer.IsOpen() && a_Size > 0)
	{
		m_RawBufferSize = a_Size;
		return true;
	}
	return false;
}

//----------------

template <class T, class H>
inline
void Synchrony<T, H>::_IncrementReaders()
{
	m_Buffer.Lock();
	m_Buffer.SharedInfo().currentReaders++;
	m_Buffer.Unlock();
}

template <class T, class H>
inline
void Synchrony<T, H>::_DecrementReaders(bool a_bSignalRead)
{
	m_Buffer.Lock();
	m_Buffer.SharedInfo().currentReaders--;
	if (a_bSignalRead)
	{
		m_Buffer.SetAsRead();
	}
	m_Buffer.Unlock();
}

template <class T, class H>
bool Synchrony<T, H>::_ReadNewest(bool a_bWait, SynchroSample<T, H>& a_rSample)
{
	if (m_Buffer.IsOpen()) // Synapse is shared
	{
		// signal read access to buffer
		_IncrementReaders();
		// grab pointer to shared data
		char* pData = m_Buffer.GrabPointer();
		// global information about the Synchrony object is at the start of the data
		SynchroInfo* pInfo = reinterpret_cast<SynchroInfo*>(pData);
		// oops... somebody cleared the buffer!
		if (pInfo->pushed < m_PoppedId)
		{
			m_PoppedId = pInfo->pushed;
		}
		// if the "next" sample isn't available yet...
		if (a_bWait) // wait
		{
			while (!pInfo->samples || (pInfo->pushed == m_PoppedId))
			{
				// not reading but waiting
				_DecrementReaders();
				if (m_Buffer.WaitForNew()) 
				{
					// reading again
					_IncrementReaders();
				}
				else // waiting has been interrupted
				{
					return false;
				}
			}
		}
		else if (!pInfo->samples) // buffer must contain samples
		{
			// signal reading stop
			_DecrementReaders();
			return false;
		}
		pData += sizeof(SynchroInfo);
		// this is followed by the actual samples...
		pData += pInfo->slots * sizeof(SynchroSlot);
		// get newest sample offset
		char* pStart = pData + pInfo->newestIndex;
		// copy sample
		a_rSample.ConvertFromCharWrap(pData, pStart, pData + m_RawBufferSize);
		// update popped state
		AddWrap(pStart, GetSampleHeaderSize() + a_rSample.GetSize() * sizeof(T), pData, pData + m_RawBufferSize);
		m_PoppedIndex = static_cast<long>(pStart - pData);
		m_PoppedId = a_rSample.GetId() + 1;
		_DecrementReaders();
		return true;
	}
	return false;
}

template <class T, class H>
bool Synchrony<T, H>::_ReadNext(bool a_bWait, SynchroSample<T, H>& a_rSample)
{
	if (m_Buffer.IsOpen()) // Synapse is shared
	{
		// signal read access to buffer
		_IncrementReaders();
		// grab pointer to shared data
		char* pData = m_Buffer.GrabPointer();
		// global information about the Synchrony object is at the start of the data
		SynchroInfo* pInfo = reinterpret_cast<SynchroInfo*>(pData);
		pData += sizeof(SynchroInfo);
		// the timeline (array of SynchroSlots) follows the global information
		SynchroSlot* pSlots = reinterpret_cast<SynchroSlot*>(pData);
		// this is followed by the actual samples
		pData += pInfo->slots * sizeof(SynchroSlot);
		// oops... somebody cleared the buffer!
		if (pInfo->pushed < m_PoppedId)
		{
			m_PoppedId = pInfo->pushed;
		}
		// if the "next" sample isn't available yet...
		if (a_bWait) // wait
		{
			while (!pInfo->samples || (pInfo->pushed == m_PoppedId))
			{
				// not reading but waiting
				_DecrementReaders();
				if (m_Buffer.WaitForNew()) 
				{
					// reading again
					_IncrementReaders();
				}
				else // waiting has been interrupted
				{
					return false;
				}
			}
		}
		else if (pInfo->pushed == m_PoppedId) // nothing new yet
		{
			// signal reading stop
			_DecrementReaders();
			return false;
		}
		// locate statistics for the oldest sample in the buffer
		H oldestSampleInfo;
		oldestSampleInfo.ConvertFromCharWrap(pData, pData + (pSlots + pInfo->oldestSlot)->index, pData + m_RawBufferSize);
		// if the "next" sample has already been overwritten...
		if (oldestSampleInfo.id >= m_PoppedId)
		{
			// retrieve oldest sample instead
			a_rSample.ConvertFromCharWrap(pData, pData + (pSlots + pInfo->oldestSlot)->index, pData + m_RawBufferSize);
			// reset pop index
			//m_PoppedIndex = 0;
			m_PoppedIndex = (pSlots + pInfo->oldestSlot)->index;
		}
		else 
		{
			a_rSample.ConvertFromCharWrap(pData, pData + m_PoppedIndex, pData + m_RawBufferSize);
		}
		if (a_rSample.GetSize() == 0)
		{
//			a_rSample.ConvertFromCharWrap(pData, pData + m_PoppedIndex, pData + m_RawBufferSize);
			_DecrementReaders();
			return false;
		}
		// updated popped state
		AddWrap(m_PoppedIndex, GetSampleHeaderSize() + a_rSample.GetSize() * sizeof(T), 0L, m_RawBufferSize);	
		m_PoppedId = a_rSample.GetId() + 1;
		// signal reading stop
		_DecrementReaders();
		return true;
	}
	return false;
}

template <class T, class H>
bool Synchrony<T, H>::_ReadAtTime(bool a_bWait, SynchroSample<T, H>& a_rSample, long a_StartTime, long a_Index)
{
	if (m_Buffer.IsOpen()) // Synapse is shared
	{
		// signal read access to buffer
		_IncrementReaders();
		// grab pointer to shared data
		char* pData = m_Buffer.GrabPointer();
		// global information about the Synchrony object is at the start of the data
		SynchroInfo* pInfo = reinterpret_cast<SynchroInfo*>(pData);
		pData += sizeof(SynchroInfo);
		// the timeline (array of SynchroSlots) follows the global information
		SynchroSlot* pSlots = reinterpret_cast<SynchroSlot*>(pData);
		// this is followed by the actual samples...
		pData += pInfo->slots * sizeof(SynchroSlot);
		// temporal offset from earliest time in buffer
		long long diffTime = a_StartTime - pInfo->oldestSlotTime;
		// time can't be from the forgotten past
		if (diffTime >= 0)
		{
			if (a_bWait)
			{
				while (!pInfo->samples || (a_StartTime >= pInfo->newestSlotTime + pInfo->interval))
				{
					_DecrementReaders();
					if (m_Buffer.WaitForNew()) 
					{
						_IncrementReaders();
					}
					else // waiting has been interrupted
					{
						return false;
					}
				}
			}
			else if (a_StartTime >= pInfo->newestSlotTime + pInfo->interval)
			{
				_DecrementReaders();
				return false;
			}
			// determine the offset in terms of slots
			long n = static_cast<long>(diffTime / pInfo->interval) % pInfo->slots;
			// find the slot that matches the specified time
			long actual_n = pInfo->oldestSlot;
			AddWrap(actual_n, n, 0L, pInfo->slots);
			// enough samples in the time slot?
			if (a_Index < pSlots[actual_n].count)
			{
				char* pStart = pData + pSlots[actual_n].index;
				for (long i = 0; i < a_Index; i++)
				{
					H sampleInfo;
					sampleInfo.ConvertFromCharWrap(pData, pStart, pData + m_RawBufferSize);
					AddWrap(pStart, GetSampleHeaderSize() + sampleInfo.size * sizeof(T), pData, pData + m_RawBufferSize);
				}
				// copy it into the sample object
				a_rSample.ConvertFromCharWrap(pData, pStart, pData + m_RawBufferSize);
				// updated popped state
				AddWrap(pStart, GetSampleHeaderSize() + a_rSample.GetSize() * sizeof(T), pData, pData + m_RawBufferSize);
				m_PoppedIndex = static_cast<long>(pStart - pData);
				m_PoppedId = a_rSample.GetId() + 1;
				// exit successfully
				_DecrementReaders();
				return true;
			}
		}
		_DecrementReaders();
	}
	return false;
}

template <class T, class H>
bool Synchrony<T, H>::_ReadFirstAtTimeOrPrevious(SynchroSample<T, H>& a_rSample, long a_StartTime)
{
	if (m_Buffer.IsOpen()) // Synapse is shared
	{
		// signal read access to buffer
		_IncrementReaders();
		// grab pointer to shared data
		char* pData = m_Buffer.GrabPointer();
		// global information about the Synchrony object is at the start of the data
		SynchroInfo* pInfo = reinterpret_cast<SynchroInfo*>(pData);
		pData += sizeof(SynchroInfo);
		// the timeline (array of SynchroSlots) follows the global information
		SynchroSlot* pSlots = reinterpret_cast<SynchroSlot*>(pData);
		// this is followed by the actual samples...
		pData += pInfo->slots * sizeof(SynchroSlot);
		// temporal offset from earliest time in buffer
		long long diffTime = a_StartTime - pInfo->oldestSlotTime;
		// time can't be from the forgotten past
		if (diffTime >= 0)
		{
			long n, actual_n;
			// if time is beyond newest time slot, target newest time slot instead
			if (a_StartTime >= pInfo->newestSlotTime + pInfo->interval)
			{
				n = pInfo->slots-1;
			}
			else
			{
				// determine the offset in terms of slots
				n = static_cast<long>(diffTime / pInfo->interval) % pInfo->slots;
			}
			// find the slot that matches the specified time
			actual_n = pInfo->oldestSlot;
			AddWrap(actual_n, n, 0L, pInfo->slots);
			// check that slot for any samples
			char* pStart;
			if (pSlots[actual_n].count)
			{
				pStart = pData + pSlots[actual_n].index;
			}
			else // no samples...
			{
				do
				{
					// search backwards through time slots for one with samples in it
					if (n-- == 0)
					{
						break; // quit when passing oldest time slot
					}
					// find the slot that matches the specified time
					actual_n = pInfo->oldestSlot;
					AddWrap(actual_n, n, 0L, pInfo->slots);
				} while (!pSlots[actual_n].count);
				// no time slot with samples in it?
				if (n < 0)
				{
					_DecrementReaders();
					return false;
				}
				else // get the last sample in that time slot
				{
					pStart = pData + pSlots[actual_n].index;
					for (long i = pSlots[actual_n].count; i > 1; i--)
					{
						H sampleInfo;
						sampleInfo.ConvertFromCharWrap(pData, pStart, pData + m_RawBufferSize);
						AddWrap(pStart, GetSampleHeaderSize() + sampleInfo.size * sizeof(T), pData, pData + m_RawBufferSize);
					}
				}
			}
			a_rSample.ConvertFromCharWrap(pData, pStart, pData + m_RawBufferSize);
			// updated popped state
			AddWrap(pStart, GetSampleHeaderSize() + a_rSample.GetSize() * sizeof(T), pData, pData + m_RawBufferSize);
			m_PoppedIndex = static_cast<long>(pStart - pData);
			m_PoppedId = a_rSample.GetId() + 1;
			// exit successfully
			_DecrementReaders();
			return true;
		}
		_DecrementReaders();
	}
	return false;
}

template <class T, class H>
bool Synchrony<T, H>::_ReadAllAtTime(bool a_bWait, vector<SynchroSample<T, H> >& a_rSampleArray, long a_StartTime)
{
	if (m_Buffer.IsOpen()) // Synapse is shared
	{
		// signal read access to buffer
		_IncrementReaders();
		// grab pointer to shared data
		char* pData = m_Buffer.GrabPointer();
		// global information about the Synchrony object is at the start of the data
		SynchroInfo* pInfo = reinterpret_cast<SynchroInfo*>(pData);
		pData += sizeof(SynchroInfo);
		// the timeline (array of SynchroSlots) follows the global information
		SynchroSlot* pSlots = reinterpret_cast<SynchroSlot*>(pData);
		// this is followed by the actual samples...
		pData += pInfo->slots * sizeof(SynchroSlot);
		// temporal offset from earliest time in buffer
		long long diffTime = a_StartTime - pInfo->oldestSlotTime;
		// time has to be within range of what is currently indexed
		if (diffTime >= 0)
		{
			if (a_bWait)
			{
				while (!pInfo->samples || (a_StartTime >= pInfo->newestSlotTime + pInfo->interval))
				{
					_DecrementReaders();
					if (m_Buffer.WaitForNew()) 
					{
						_IncrementReaders();
					}
					else // waiting has been interrupted
					{
						return false;
					}
				}
			}
			else if (a_StartTime >= pInfo->newestSlotTime + pInfo->interval)
			{
				_DecrementReaders();
				return false;
			}
			// determine the offset in terms of slots
			long n = static_cast<long>(diffTime / pInfo->interval) % pInfo->slots;
			// find the slot that matches the specified time
			long actual_n = pInfo->oldestSlot;
			AddWrap(actual_n, n, 0L, pInfo->slots);
			if (pSlots[actual_n].count)
			{
				char* pStart = pData + pSlots[actual_n].index;
				for (long i = 0; i < pSlots[actual_n].count; i++)
				{
					SynchroSample<T, H> sample;
					sample.ConvertFromCharWrap(pData, pStart, pData + m_RawBufferSize);
					a_rSampleArray.push_back(sample);
					AddWrap(pStart, GetSampleHeaderSize() + sample.GetSize() * sizeof(T), pData, pData + m_RawBufferSize);
				}
				// updated popped state
				m_PoppedIndex = static_cast<long>(pStart - pData);
				m_PoppedId = a_rSampleArray.back().GetId() + 1;
				// exit successfully
				_DecrementReaders();
				return true;
			}
		}
		_DecrementReaders();
	}
	return false;
}

//-----------------------------------------------------------------------------

#endif